/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.phoenix.repository.query.sql;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Set;

import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.query.Row;

import net.phoenix.repository.RepositoryRuntimeException;
import net.phoenix.repository.dao.DatabaseException;

/**
 * Jigsaw server
 * 
 * @author <a href="shamphone@gmail.com">Li XiongFeng</a>
 * @date 2012-1-31
 * @version 1.0.0
 */
class ResultSetRow extends AbstractMap<String, Object> implements Row {
	private ResultSet row;
	private Session session;
	private ResultSetMetaData meta;

	public ResultSetRow(Session session, ResultSet row) {
		this.row = row;
		this.session = session;
		try {
			this.meta = row.getMetaData();
		} catch (SQLException e) {
			throw new DatabaseException(e);
		}
	}

	@Override
	public Value[] getValues() throws RepositoryException {
		return null;
	}

	@Override
	public Value getValue(String columnName) throws ItemNotFoundException, RepositoryException {
		int index = this.getColumnIndex(columnName);
		try {
			switch (meta.getColumnType(index)) {
			case Types.BOOLEAN:
				return session.getValueFactory().createValue(row.getBoolean(index));
			case Types.CHAR:
				return session.getValueFactory().createValue(row.getByte(index) == '1');
			case Types.DOUBLE:
			case Types.FLOAT:
				return session.getValueFactory().createValue(row.getDouble(index));
			case Types.NUMERIC:
				if (meta.getScale(index) == 0)
					return session.getValueFactory().createValue(row.getLong(index));
				else
					return session.getValueFactory().createValue(row.getDouble(index));
			case Types.INTEGER:
			case Types.BIGINT:
				return session.getValueFactory().createValue(row.getLong(index));
			case Types.DATE:
				Date date = row.getDate(index);
				if (date != null) {
					Calendar time = Calendar.getInstance();
					time.setTime(date);
					return session.getValueFactory().createValue(time);
				}
				return session.getValueFactory().createValue((Calendar) null);
			case Types.TIME:
				Time stime = row.getTime(index);
				if (stime != null) {
					Calendar time = Calendar.getInstance();
					time.setTime(stime);
					return session.getValueFactory().createValue(time);
				}
				return session.getValueFactory().createValue((Calendar) null);
			case Types.TIMESTAMP:
				Object obj = row.getObject(index);
				if (obj != null) {
//					if (obj instanceof oracle.sql.TIMESTAMP) {
//						Timestamp ts = ((oracle.sql.TIMESTAMP) obj).timestampValue();
//						if (ts != null) {
//							Calendar time = Calendar.getInstance();
//							time.setTime(ts);
//							return session.getValueFactory().createValue(time);
//						}
//					} else {
						Timestamp ts = (Timestamp) obj;
						Calendar time = Calendar.getInstance();
						time.setTime(ts);
						return session.getValueFactory().createValue(time);
					//}
				}
				return session.getValueFactory().createValue((Calendar) null);
			default:
				return session.getValueFactory().createValue(row.getString(index));
			}
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	private int getColumnIndex(String columnName) {
		try {
			for (int i = 1; i < meta.getColumnCount() + 1; i++)
				if (meta.getColumnLabel(i).equalsIgnoreCase(columnName))
					return i;
			throw new IllegalArgumentException("Unknow column name " + columnName);
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public Node getNode() throws RepositoryException {
		return this.getNode("pkid");
	}

	@Override
	public Node getNode(String selectorName) throws RepositoryException {
		try {
			return session.getNodeByIdentifier(this.row.getString("pkid"));
		} catch (SQLException e) {
			throw new RepositoryException(e);
		}
	}

	@Override
	public String getPath() throws RepositoryException {
		return this.getNode().getPath();
	}

	@Override
	public String getPath(String selectorName) throws RepositoryException {
		return this.getNode(selectorName).getPath();
	}

	@Override
	public double getScore() throws RepositoryException {
		return 0;
	}

	@Override
	public double getScore(String selectorName) throws RepositoryException {
		return 0;
	}

	@Override
	public int size() {
		try {
			return meta.getColumnCount();
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public boolean isEmpty() {
		try {
			return meta.getColumnCount() > 0;
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public boolean containsKey(Object key) {
		String columnName = key.toString();
		try {
			for (int i = 1; i < meta.getColumnCount() + 1; i++)
				if (meta.getColumnName(i).equalsIgnoreCase(columnName))
					return true;
			return false;
		} catch (SQLException ex) {
			throw new DatabaseException(ex);
		}
	}

	@Override
	public Object get(Object key) {
		try {
			return this.getValue(key.toString());
		} catch (RepositoryException e) {
			throw new RepositoryRuntimeException(e);
		}
	}

	@Override
	public Object remove(Object key) {
		throw new UnsupportedOperationException();
	}

	@Override
	public Set<java.util.Map.Entry<String, Object>> entrySet() {
		return new MetaDataEntrySet();
	}

	class MetaDataEntrySet extends AbstractSet<java.util.Map.Entry<String, Object>> {

		@Override
		public Iterator<java.util.Map.Entry<String, Object>> iterator() {
			return new MetaDataEntryIterator();
		}

		@Override
		public int size() {
			try {
				return meta.getColumnCount();
			} catch (SQLException e) {
				throw new DatabaseException(e);
			}
		}

	}

	class MetaDataEntryIterator implements Iterator<Entry<String, Object>> {
		private int count = 1;

		public MetaDataEntryIterator() {
			this.count = 0;
		}

		@Override
		public boolean hasNext() {
			try {
				return this.count < meta.getColumnCount();
			} catch (SQLException e) {
				throw new DatabaseException(e);
			}
		}

		@Override
		public Entry<String, Object> next() {

			count++;
			return new Entry<String, Object>() {

				@Override
				public String getKey() {
					try {
						return meta.getColumnName(count);
					} catch (SQLException e) {
						throw new DatabaseException(e);
					}
				}

				@Override
				public Object getValue() {
					try {
						return get(meta.getColumnName(count));
					} catch (SQLException e) {
						throw new DatabaseException(e);
					}
				}

				@Override
				public Object setValue(Object value) {
					throw new UnsupportedOperationException();
				}
			};

		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}

	}

}
